package jtt808

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"hy_808/tcpx"
	"log"
)

const (
	MaxPackageLength = 3000 // 包的最大长度
	MARK_CODE        = 0x7e //808协议包分割标识
)

// 分割包 7exxxxx7e7exxxx7e
func Split(seg []byte, sp []byte) ([][]byte, []byte) {
	//fmt.Println("新包", hex.EncodeToString(seg), ",余包", hex.EncodeToString(sp))
	seg = append(sp, seg...)
begin:
	last := 0 // 包最后得索引
	head := 0
	curr := 0 // 正在检查的字符的位置
	mark := false
	var many [][]byte  //多个包组合
	var single []byte  //完整一个包
	var surplus []byte //本次剩余字节数组
	for i, v := range seg {
		//next = i
		if v == MARK_CODE {
			if curr == 0 { //重新开始的一个包
				//buf = append(buf, v)
				head = i
			} else {
				last = i
				mark = true
				break
			}
		} else {
			mark = false
		}
		curr = i
	}
	if mark {
		single = seg[head : last+1]
		//转义还原
		bytes.ReplaceAll(single[1:len(single)-1], []byte{0x7d, 0x02}, []byte{0x7e})
		bytes.ReplaceAll(single[1:len(single)-1], []byte{0x7d, 0x01}, []byte{0x7d})
		//校验码校验
		ev := single[len(single)-2]
		//middleData := buf[1 : len(buf)-2]
		//log.Println("中间需要校验得数据", hex.EncodeToString(middleData))
		av := CheckCodeCheck(single[1 : len(single)-2])
		if av != ev {
			log.Println("校验码校验失败,丢弃该包", "原校验码", ev, "计算校验码", av, hex.EncodeToString(single))
		} else {
			many = append(many, single)
		}

		if len(seg) > last+1 { //有剩余包
			seg = seg[last+1:]
			goto begin //继续累计一下包
		}
	} else {
		surplus = seg[last+1:]
	}

	return many, surplus
}

type ReceiveHandler struct {
	conn    *tcpx.Conn
	surplus []byte
	handle  func(c *tcpx.Conn, p Jtt808) bool
}

func (h *ReceiveHandler) OnReceive(data []byte) bool {
	//defer func() {
	//	if info := recover(); info != nil {
	//		h.conn.Error.Println("处理接收包时出现panic", info)
	//	}
	//}()
	var buf [][]byte
	buf, h.surplus = Split(data, h.surplus)
	//log.Println("解粘包个数", len(buf))
	for _, b := range buf {
		if b[len(b)-1] == EndId {
			//log.Println("处理完整一帧：", hex.EncodeToString(b))
			h.handle(h.conn, MakeJtt808Packet(b))
		} else {
			//h.surplus = buf
			log.Println("该帧异常：", hex.EncodeToString(b))
		}
	}

	return true
}

/*
对接tcpx.MakeReceiveHandler的方法
当收到数据时，tcpx.Conn将数据交给本包的ReceiveHandler分包，然后将包交给传入的h函数处理
*/
func ReceiveProvider(h func(c *tcpx.Conn, p Jtt808) bool) tcpx.BuildReceiveHandler {
	return func(c *tcpx.Conn) tcpx.ReceiveHandler {
		return (&ReceiveHandler{
			conn:   c,
			handle: h,
		}).OnReceive
	}
}

func MakeJtt808Packet(origin []byte) (p Jtt808) {
	data := origin[1 : len(origin)-1] //去掉头尾 0x7e
	p.Head = Head{}
	//消息头标识
	headId := binary.BigEndian.Uint16(data[0:2])
	p.Head.SetId(headId)
	//消息体属性
	attr := binary.BigEndian.Uint16(data[2:4])
	bodyLen, encrypt, packCut := ParseAttribute(attr)
	p.Head.SetAttribute(bodyLen, encrypt, packCut)
	//终端手机号
	phone := Bcd2Number(data[4:10])
	p.Head.SetPhone(phone)
	//流水号
	no := binary.BigEndian.Uint16(data[10:12])
	p.Head.SetSeriaNo(no)
	//长信息 需要分包 (暂时用不到)
	if packCut == 1 {
		allCutBagNum := binary.BigEndian.Uint16(data[12:14])
		cutBagNo := binary.BigEndian.Uint16(data[14:16])
		p.Head.SetPotting(allCutBagNum, cutBagNo)
	}
	//校验码
	p.CheckCode = data[len(data)-1]
	//消息体
	if packCut != 1 { //如果不是长消息 不用分包
		p.SetBody(data[12 : 12+bodyLen])
	}
	//完整一包
	p.Data = origin
	return p

}

func ParseAttribute(attr uint16) (bodyLen, encrypt, packCut int16) {
	bodyLenMask := 1023 //掩码 使用与运算 计算消息体长度（00000011 11111111）
	bodyLen = int16(attr) & int16(bodyLenMask)

	encryptMask := 7168 // 计算加密方式的掩码（00011100 00000000）
	encrypt = int16(attr) & int16(encryptMask)

	packCutMask := 8192 // 计算加密方式的掩码（00100000 00000000）
	packCut = int16(attr) & int16(packCutMask)
	return
}
